package cn.silver.framework.mybatis.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.silver.framework.core.enums.SystemStatusEnums;
import cn.silver.framework.core.exception.ServiceException;
import cn.silver.framework.core.utils.TreeBuildUtils;
import cn.silver.framework.mybatis.annotation.TableField;
import cn.silver.framework.mybatis.bean.TreeBean;
import cn.silver.framework.mybatis.domain.TreeEntity;
import cn.silver.framework.mybatis.mapper.TreeMapperPlus;
import cn.silver.framework.mybatis.param.TreeParam;
import cn.silver.framework.mybatis.service.ITreeService;
import cn.silver.framework.mybatis.utils.TreeNodeUtil;
import cn.silver.framework.openapi.constant.ResultEnum;
import cn.silver.framework.web.model.TreeModel;
import cn.silver.framework.web.model.TreeResult;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author zhouxiaobo
 */
public class TreeServiceImpl<M extends TreeMapperPlus<T, V>, T extends TreeEntity, P extends TreeParam, V extends TreeBean<V>>
        extends BusServiceImpl<M, T, P, V> implements ITreeService<T, P, V> {
    @Override
    public List<V> selectTree(P params) {
        return this.selectTree(this.buildQueryWrapper(params));
    }

    @Override
    public List<V> selectTree(Wrapper<T> wrapper) {
        List<V> models = this.selectList(wrapper);
        Map<String, List<V>> modelMap = models.stream().collect(Collectors.groupingBy(V::getPid));
        models.stream().filter(item -> modelMap.containsKey(item.getId())).forEach(item -> item.setChildren(modelMap.get(item.getId())));
        return models.stream().filter(item -> TreeEntity.ROOT_NODE.equalsIgnoreCase(item.getPid())).toList();
    }

    @Override
    public List<Tree<String>> buildTreeSelect(List<V> models) {
        if (CollUtil.isEmpty(models)) {
            return CollUtil.newArrayList();
        }
        List<Tree<String>> trees = TreeBuildUtils.build(models, (model, tree) ->
                tree.setId(model.getId())
                        .setParentId(model.getPid())
                        .setName(model.getName())
                        .setWeight(model.getSort()));
//        List<Tree<String>> trees = new ArrayList<>();
//        Map<String, List<V>> modelMap = models.stream().collect(Collectors.groupingBy(V::getPid));
//        modelMap.entrySet().stream().forEach(entry -> {
//            trees.addAll();
//        });
        return trees;
    }

    @Override
    public TreeResult getTreeSelect(P param) {
        TreeResult result = new TreeResult();
        List<V> models = this.selectList(param);
        result.setKeys(models.stream().map(V::getId).toList());
        result.setDicts(models.stream().map(V::convertDict).toList());
        List<TreeModel> treeModels = models.stream().map(V::convertTree).toList();
        Map<String, List<TreeModel>> treeMap = treeModels.stream().collect(Collectors.groupingBy(TreeModel::getPid));
        treeModels.stream().filter(item -> treeMap.containsKey(item.getId())).forEach(item -> {
            item.setChildren(treeMap.get(item.getId()));
            item.setHasChild(Boolean.TRUE);
        });
        result.setTrees(treeModels.stream().filter(item -> TreeEntity.ROOT_NODE.equalsIgnoreCase(item.getPid())).toList());
        return result;
    }

    @Override
    protected Long selectExist(V model) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.ne(StringUtils.isNotBlank(model.getId()), "id", model.getId());
        queryWrapper.eq(StringUtils.isNotBlank(model.getPid()), "pid", model.getPid());
        queryWrapper.and(wrapper -> {
            wrapper.eq(StringUtils.isNotBlank(model.getCode()), "code", model.getCode());
            wrapper.or().eq(StringUtils.isNotBlank(model.getName()), "name", model.getName());
            List<Field> fields = Arrays.stream(model.getClass().getDeclaredFields()).filter(item -> item.isAnnotationPresent(TableField.class)).toList();
            for (Field field : fields) {
                TableField tableField = field.getAnnotation(TableField.class);
                if (tableField.unique()) {
                    wrapper.or().eq(StringUtils.isNotBlank(field.getName()), tableField.name(), model.getId());
                }
            }
        });
        return this.baseMapper.selectCount(queryWrapper);
    }

    @Override
    public void checkUnique(V model) {
        if (StringUtils.isBlank(model.getPid())) {
            model.setPid(TreeEntity.ROOT_NODE);
        }
        if (model.getPid().equals(model.getId())) {
            throw new ServiceException(ResultEnum.DATA_VALIDATED_FAILED.getCode(), "当前节点不能和上级节点一致");
        }
        if (!TreeEntity.ROOT_NODE.equals(model.getPid())) {
            T parent = this.baseMapper.selectById(model.getPid());
            if (parent == null) {
                throw new ServiceException(ResultEnum.NOT_FOUND.getCode(), "上级节点不存在，无法保存节点数据");
            } else if (SystemStatusEnums.BLOCK.getCode().equalsIgnoreCase(parent.getStatus())) {
                throw new ServiceException(ResultEnum.DATA_VALIDATED_FAILED.getCode(), "上级节点锁定，无法保存节点数据");
            } else if (SystemStatusEnums.DISABLED.getCode().equalsIgnoreCase(parent.getStatus())) {
                throw new ServiceException(ResultEnum.DATA_VALIDATED_FAILED.getCode(), "上级节点停用，无法保存节点数据");
            }
            model.setPnode(parent.getNode());
        }
        long count = this.selectExist(model);
        if (count > 0) {
            throw new ServiceException(ResultEnum.DATA_ERROR_EXIST.getCode(), "数据保存失败，存在重复记录");
        }
        model.setNode(this.executeNode(model));
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public boolean insert(V model) {
//        this.checkUnique(model);
        return super.insert(model);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public boolean update(V model) {
//        this.checkUnique(model);
        return super.update(model);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public int remove(String id) {
        return super.remove(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public int removeBatch(Collection<String> ids) {
        return super.removeBatch(ids);
    }

    @Override
    public QueryWrapper<T> buildQueryWrapper(P params) {
        QueryWrapper<T> wrapper = super.buildQueryWrapper(params);
        wrapper.eq(StringUtils.isNotBlank(params.getPid()), "pid", params.getPid());
        wrapper.orderByAsc("pid", "sort");
        return wrapper;
    }

    public String executeNode(V model) {
        /*
         * 分成三种情况
         * 1.数据库无数据 调用YouBianCodeUtil.getNextYouBianCode(null);
         * 2.添加子节点，无兄弟元素 YouBianCodeUtil.getSubYouBianCode(parentCode,null);
         * 3.添加子节点有兄弟元素 YouBianCodeUtil.getNextYouBianCode(lastCode);
         * */
        //找同类 确定上一个最大的code值
        V exist = this.selectById(model.getId());
        if (exist != null && model.getPid().equals(exist.getPid()) && StringUtils.isNotBlank(model.getNode())) {
            return exist.getNode();
        }
        String node;
        QueryWrapper<T> query = new QueryWrapper<T>().eq("pid", model.getPid())
                .isNotNull("node").ne("node", "null");
        if (StringUtils.isNotBlank(model.getPnode())) {
            query.like("node", model.getPnode());
        }
        query.orderByDesc("node");
        List<T> list = baseMapper.selectList(query);
        if (CollectionUtils.isEmpty(list)) {
            if (TreeEntity.ROOT_NODE.equalsIgnoreCase(model.getPid())) {
                //情况1
                node = TreeNodeUtil.getNextYouBianCode(null);
            } else {
                //情况2
                node = TreeNodeUtil.getSubYouBianCode(model.getPnode(), null);
            }
        } else {
            //情况3
            node = TreeNodeUtil.getNextYouBianCode(list.get(0).getNode());
        }
        return node;
    }
}
